/*	$Id: pkt.c 7 2008-07-29 02:58:11Z phrakt $	*/
/*
 * Copyright (c) 2003 Jean-Francois Brousseau <jfb@openbsd.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <sys/param.h>

#include <err.h>
#include <stdio.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>

#include "pkt.h"
#include "expr.h"
#include "pbuf.h"
#include "pforge.h"


/*
 * pf_pkt_alloc()
 *
 * Allocate a new packet structure.
 * Returns a pointer to the newly allocated structure, or NULL on failure.
 */
PF_PKT*
pf_pkt_alloc(void)
{
	PF_PKT *pkt;

	pkt = (PF_PKT *)pf_malloc(sizeof(*pkt));
	if (pkt == NULL) {
		pf_seterr(PF_ERR_NOMEM);
		return (NULL);
	}
	memset(pkt, 0, sizeof(*pkt));

	PFPCHAIN_INIT(&(pkt->pkt_bufs));
	TAILQ_INIT(&(pkt->pkt_tags));

	return (pkt);
}

/*
 * pf_pkt_free()
 *
 * Free the packet pointed to by <vp>.  The packet is returned to the pool of
 * unused packets for future reuse.
 */
void
pf_pkt_free(void *vp)
{
	struct pf_pkt_tag *tag;
	PF_PKT *pkt;

	if (vp != NULL) {
		pkt = (PF_PKT *)vp;

		while (!TAILQ_EMPTY(&(pkt->pkt_tags))) {
			tag = TAILQ_FIRST(&(pkt->pkt_tags));
			TAILQ_REMOVE(&(pkt->pkt_tags), tag, pt_link);
			pf_free(tag);

		}


		pf_free(pkt);
	}
}

/*
 * pf_pkt_clear()
 *
 * Clear the contents of the packet and reinitialize its fields.
 *
 */
int
pf_pkt_clear(PF_PKT *pkt)
{
	pkt->pkt_len = 0;

	return (0);
}

/*
 * pf_pkt_set()
 *
 * Set the packet's contents to <data>, which is <len> bytes in length.
 * Any previous packet data is erased.
 */
int
pf_pkt_set(PF_PKT *pkt, const void *data, size_t len)
{
	int i, flags, nbuf;
	size_t cpsz, left;
	const void *dp;
	PF_PBUF *pbuf;

	left = len;
	pf_pkt_clear(pkt);

	/* allocate enough pbufs to store the whole packet */
	nbuf = len / PFPBUF_DATA_SIZE;
	if (len % PFPBUF_DATA_SIZE)
		nbuf++;

	nbuf -= PFPCHAIN_LEN(&(pkt->pkt_bufs));

	for (i = 0; i < nbuf; i++) {
		pbuf = pf_pbuf_alloc(0);
		PFPCHAIN_APPEND(&(pkt->pkt_bufs), pbuf);
	}

	dp = data;
	PFPCHAIN_FOREACH(pbuf, &(pkt->pkt_bufs)) {
		cpsz = MIN(left, sizeof(pbuf->pb_data));
		memcpy(pbuf->pb_data, dp, cpsz);
		pbuf->pb_hdr.pb_len = cpsz;
		pbuf->pb_hdr.pb_start = pbuf->pb_data;
		left -= cpsz;
		if (left == 0)
			break;
	}
	pkt->pkt_len = len;

	return (0);
}

/*
 * pf_pkt_prepend()
 *
 * Prepend <len> bytes of data found at <data> to the packet <pkt>.
 */
int
pf_pkt_prepend(PF_PKT *pkt, const void *data, size_t len)
{
	return (0);
}


/*
 * pf_pkt_append()
 *
 * Append <len> bytes of data found at <data> to the packet <pkt>.
 */
int
pf_pkt_append(PF_PKT *pkt, const void *data, size_t len)
{
	return (0);
}


/*
 * pf_pkt_copy()
 *
 * Copy the contents of the packet <pkt> starting at offset <off> into the
 * destination buffer <buf>, which can accept up to <len> bytes.
 * Returns the number of bytes successfully copied in the buffer, or -1 on
 * failure.
 */
ssize_t
pf_pkt_copy(const PF_PKT *pkt, void *dst, size_t len, size_t off)
{
	ssize_t res;


	return (res);
}


/*
 * pf_pkt_tag()
 *
 * Place a tag on the packet <pkt> at offset <off> and with associated
 * protocol <proto>.
 * Returns 0 on success, or -1 on failure.
 */
int
pf_pkt_tag(PF_PKT *pkt, size_t off, int proto, void *data)
{
	struct pf_pkt_tag *tag;

	if (off > pkt->pkt_len) {
		pf_seterr(PF_ERR_RANGE);
		return (-1);
	}

	if ((tag = (struct pf_pkt_tag *)pf_malloc(sizeof(*tag))) == NULL) {
		pf_seterr(PF_ERR_NOMEM);
		return (-1);
	}

	tag->pt_off = off;
	tag->pt_proto = proto;
	tag->pt_data = data;

	TAILQ_INSERT_TAIL(&(pkt->pkt_tags), tag, pt_link);

	return (0);
}

/*
 * pf_pkt_eval()
 *
 * Evaluate the packet <pkt> against the expression <expr>.
 * Returns 1 if the packet matched the expression, 0 if it did not, or -1 if
 * an error occured.
 */
int
pf_pkt_eval(const PF_PKT *pkt, const PF_EXPR *expr)
{
	uint32_t op;
	struct pf_expr_vm vm;

	memset(&vm, 0, sizeof(vm));

	do {
		op = expr->ex_ops[vm.ev_pc];
		vm.ev_pc += 4;
#if 0
		pf_expr_runop(&vm, op);
#endif
	} while (!PFEX_VM_HALTED(&vm));


	printf("Final result: %u\n", vm.ev_acc);

}
